# -*- coding: ISO-8859-1 -*-
"""\
Convert data from the ISI Web of Science web site to BibTeX format.

Code based on EV2 parser.
"""

# Copyright (c) 2006 Kjell Magne Fauske
# See LICENSE for details.

import re
import bibtexbib
from converterbase import *
from biblibutils import *
from sets import Set

"""
(
(?P<title>.*?)
(?P<vol>\d+) \s \((?P<iss>\d+)\):\s
(?P<sp>\d+)-(?P<ep>\d+)\s
((?P<month>[a-zA-Z]{3})\s)?
((?P<day>\d{1,2})\s)?
(?P<year>\d\d\d\d)
)
"""
# regex for splitting each line into tag and data field
_tagdata_re = re.compile(r"\s*(.*?):\s*(.*)", 
                             re.MULTILINE)

# regex for splitting multiple records
#_splitrecords_re = re.compile(r'^\s*\d+\.\s*$',re.MULTILINE)

_keyword_fields = ['author keywords' ]



# Examples:
# WATER SCIENCE AND TECHNOLOGY 52 (12): 283-290 2005
# IEEE TRANSACTIONS ON ROBOTICS 22 (2): 292-307 APR 2006
# ENVIRONMENTAL SCIENCE & TECHNOLOGY 39 (24): 9440-9445 DEC 15 2005
# ROBOTICS AND AUTONOMOUS SYSTEMS 52 (2-3): 132-147 AUG 31 2005
# ACTA ASTRONAUTICA 57 (2-8): 579-593 JUL-OCT 2005


# KEY ENGINEERING MATERIALS 306-308: 229-234 Part 1-2 2006

_sourceext = r"""
(
(?P<journal>.*?)
((?P<volume>\d+) \s \((?P<issue>.*?)\))?:\s
(?P<sp>\d+)-(?P<ep>\d+)\s
((?P<month>[a-zA-Z-]*?)\s)?
((?P<day>\d{1,2})\s)?
(?P<year>\d\d\d\d)
)
"""
_sourceext_re = re.compile(_sourceext, re.VERBOSE)

# This regexp is potentially dangerous! If the first part matches
# but not the end part, the regexp may never terminate!!
_searchdataext = r"""
(
\s* ^\d\d*\. \s* (?P<authors>.*?)$
\s* ^(?P<title>.*?)$
\s* ^(?P<source>.*?)$
\s* ^Times\sCited:
)
"""

_searchdataext_re = re.compile(_searchdataext,
                            re.VERBOSE  | re.DOTALL | re.MULTILINE)

# This regexp is used to extract the records from search view
# The searchdataext_re could have been used directy, but it's safer
# to split the data extraction in two parts. This way we can ensure that
# the regexp terminates.
_recordext = r"""
(
(^\d\d*\. #\s* .*?$
.*?
 ^Times\sCited:)
)
"""

_recordext_re = re.compile(_recordext,
                            re.VERBOSE  | re.DOTALL | re.MULTILINE)
def extractISIData(src):
    """Extract bibliographic data from a copied ISI WoS web page
    
    """    
    
    # we have to detect if it's a Search result or Full record view

    # look for Full record view signature first
    is_searchview = True
    if src.find('Document Type:') >= 0:
        is_searchview = False
        return extractDetailedView(src)

    
    #if src.find('Times Cited') < 0 :
    #    return None
    # process src so it's easier to extract information
    # remove whitespace and split into lines
    lines = [line.strip() for line in src.split('\n')]
    src = "\n".join(lines)
    matches = _recordext_re.finditer(src)
    isientries = []

    for m in matches:
        entry = {}
        record = m.group(1)
        infom = _searchdataext_re.match(record)
        if not infom:
            return None
        entry = infom.groupdict()
        authors = entry.get('authors','').replace('et al.','others')
        
        entry['author(s)'] = [item.strip() for item in
                        authors.split(',') if item]
        
        # split source field
        m2 = _sourceext_re.search(entry['source'].strip())
        if m2:
            entry.update(m2.groupdict())
        isientries.append(entry)
    return isientries


    
    
def extractDetailedView(src):
    """Create a dictionary from src
    
    All keys are converted to lower case. 
    """
    isientries = []
    records = [src]
    delpar = re.compile(r'\(.*?\)')
    #if len(records) > 1:
        #records = records[1:]
    for record in records:
        fields = _tagdata_re.findall(record)
        isientry = {}
        for (tag, data) in fields:
            # remove whitespace
            if tag == 'Author(s)':
                # tmp
                # Some records list author names twice
                data = delpar.sub('', data)
                data = [item.strip() for item in data.split(',')]
            # is data of author type?
            if tag in ['KeyWords Plus','Author Keywords']:
                data = data.lower()
                data = [item.strip() for item in data.split(';')]
            if tag == 'Source':
                # split source field
                m = _sourceext_re.search(data.strip())
                if m:
                    isientry.update(m.groupdict())
            isientry[tag.lower()] = data
        isientries.append(isientry)
    #printDict(isientry)
    return isientries
    

class ISIConverter(ToBibTeXConverter):
    """Class for converting an ISI entry to BibTeX format
    
    Tries to solve the conversion problem without using an endless list of
    IFs and ELSEs.
    """
    def __init__(self, isientry, options = {}):
        # detect type
        doctype = isientry.get('document type','')
        entrytype = 'article'
        ToBibTeXConverter.__init__(self, isientry,
                bibtexbib.entrymap.get(entrytype,bibtexbib.misc_entry),options)
        self.bibtexentry.type = entrytype
        
    def abstractHandler(self):
        abstract =  self.getField('abstract')
        if abstract:
            self.bibtexentry.append('abstract', abstract)
            
    def addressHandler(self):
        address = self.getField('conference location')
        if address:
            self.bibtexentry.append('address', address)
            
    def authorHandler(self):
        # Author names in ISI have the following format:
        # Moline MA, Blackwell SM, Von Alt C, Allen B
        # or
        #  Shi Y (Shi, Yang), Qian WQ (Qian, Weiqi)
        # Not excactly the preferred BibTeX format
        author_fields = ['author(s)']
        authors, used_fields = getall(self.entry, author_fields)
        if authors:
            tmplist = []
            for author in authors:
                # split author name
                parts = author.split()
                # len(part):
                # 2 : ok
                # 3 : Von, sir, etc
                initials = parts[-1]
                if len(parts) == 2:
                    surname = parts[0]
                    vonpart = ""
                elif len(parts) == 1:
                    surname = parts[0]
                    vonpart = ""
                    initials = ""
                else:
                    surname = parts[1]
                    vonpart = parts[0]
                initialsstr = ""
                for initial in initials:
                    initialsstr += initial+'. '
                if initialsstr:
                    name = vonpart+" "+surname+", "+initialsstr
                else:
                    name = vonpart+" "+surname
                tmplist.append(name.strip())
            authors = tmplist
            self.bibtexentry.append('author',authors)
            self.unused_fields -= used_fields
    
    
    
    def ISSNHandler(self):
        issn = self.getField('issn')
        if issn:
            self.bibtexentry.append('ISSN',issn)
                    
    def journalHandler(self):
        # Todo: Include abbreviated serial title in the bibtex output?
        journal = self.getField('journal')
        if journal:
            self.bibtexentry.append('journal',capwords(journal.strip()))
            
    def keywordsHandler(self):
        # Todo: Better handling of controlled leywords and user keywords
        # Elsevier operates with controlled keywords and user keywords. 
        # How should this be handled? Separate into two different fields?
        keywords, used_fields = getall(self.entry, ['keywords plus',
                                ])
        if keywords:
            self.bibtexentry.append('keywords',keywords)
            self.unused_fields -= used_fields    

    def languageHandler(self):
        lang = self.getField('language')
##        if lang:
##            # Todo: Use Babel def. of language name
##            self.bibtexentry.append('language',lang)       
     
    def monthHandler(self):
        # inspec puts the mont in the publication date field
        # while compendex use the issue data field.
        monthstr, tmp = getfirst(self.entry,['publication date',
            'issue date', 'conference date'])
        if monthstr:
            month = extStr(monthstr)
            # Convert to BibTeX month abbrev. 
            # Note that compendex uses abbreviated month names.
            if _monthmap.has_key(month):
                month = '@'+_monthmap[month]
            self.bibtexentry.append('month',month)
     
    def numberHandler(self):
        num = extNum(self.getField('issue'));
        if num: self.bibtexentry.append('number',num)

    def pagesHandler(self):
        sp = self.getField('sp')
        ep = self.getField('ep')
        pages = (sp, ep)
        if pages:
            # Inspec sometimes use Pages: 111-18 How should this be handled?
            pages = "%s--%s" % pages
            self.bibtexentry.append('pages', pages)
    def publisherHandler(self):
        publisher = self.getField('publisher')
        if publisher:
            self.bibtexentry.append('publisher',publisher)
        
    def volumeHandler(self):
        vol = extNum(self.getField('volume'));
        if vol: self.bibtexentry.append('volume',vol)

    def yearHandler(self):
        # check compendex first
        year = self.getField('year')
        if year:
            self.bibtexentry.append('year',year)
        
                
    
    def titleHandler(self):
        # get title
        title, used_fields = getfirst(self.entry, ['title'],'')
        if title:
            self.bibtexentry.append('title',title)
            self.unused_fields -= used_fields

def convertFromString(src, options={}):
    """Extract data from src and return a list of BibTeX entries.
    
    Input:
        scr - Raw text usually pasted from the clipboard.
        options - A dict with various conversion options.
    Output:
        A list of BibTeXEntry classes.
    """
    ISIentries = extractISIData(src)
    if not ISIentries: return None
    entries = []
    for entry in ISIentries:
        t = ISIConverter(entry, options)
        t.convert()
        entries.append(t.bibtexentry)
    return entries

    
if __name__ == '__main__':
    import testdata
    #src = "\n".join([line.strip() for line in testdata.ieee7.split('\n')])
    #m = _dataext_re.search(src)
    #d =  m.groupdict()
    print "starting"
    options = {'ieeebst':True,'braceuppercase' : True, 'generatebibkey':True}
    #d = extractIEEEXData(testdata.ieeesearch)
    b = convertFromString(testdata.ieeesearch, options)
    #e = convertFromString(testdata.testdata5)
    print b[0].toString()